package org.jeecg.modules.ncmeapis.network.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.jeecg.modules.ncmeapis.network.entity.ChunkFileParam;
import org.jeecg.modules.ncmeapis.network.entity.ChunkFileParamPre;
import org.jeecg.modules.ncmeapis.network.entity.NetworkFile;
import org.jeecg.modules.ncmeapis.network.enums.UploadPath;
import org.jeecg.modules.ncmeapis.network.service.INFileService;
import org.jeecg.modules.ncmeapis.network.service.INetworkPathService;
import org.jeecg.modules.ncmeapis.network.utils.FileChunkUtil;
import org.jeecg.modules.ncmeapis.network.utils.FileDoneCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UploadServiceImpl {

    @Value("${jeecg.path.upload}")
    private String path;  //文件存储路径

    @Autowired
    private INetworkPathService networkPathService;

    private final Map<String, NetworkFile> existFiles = new HashMap<>();

    private final INFileService INFileService;

    public UploadServiceImpl(INFileService INFileService) {
        this.INFileService = INFileService;
    }

    public Map<String, Object> continueUploadPre(ChunkFileParamPre param) {
        //参数提取
        String md5 = param.getMd5();
        String service = UploadPath.ROOT;
        String type = networkPathService.getById(param.getPathId()).getPath();
        String originName = param.getFileName();
        Map<String, Object> data = new HashMap<>();
        //目标文件夹创建
        String dirPath = path + service + type;
        File dirFile = new File(dirPath);
        if (!dirFile.exists() || dirFile.isFile()) dirFile.mkdirs();
        //试图查找已上传的、非链接的文件记录
        QueryWrapper<NetworkFile> wrapper = new QueryWrapper<>();
        wrapper.eq("md5", md5).eq("is_link", 0).last("limit 1");
        NetworkFile networkFile = INFileService.getOne(wrapper);
        //上传过该文件
        if (networkFile != null) {
            //是否是同一个服务下的同一个类别
            boolean isTheSameDir = networkFile.getService().equals(service) && networkFile.getType().equals(type);
            //只有在isTheSameDir为false的时候才用得上的预用文件
            NetworkFile newNetworkFile = new NetworkFile(networkFile, originName, service, type);
            if (networkFile.getComplete()) {
                //如果完成，告诉前端跳过上传
                data.put("skipUpload", true);
                data.put("md5", networkFile.getMd5());
                data.put("url", networkFile.getService() + File.separator + networkFile.getType() + File.separator + networkFile.getStoreName());
                //文件传完了
                newNetworkFile.setComplete(true);
            } else {
                //如果未完成，给前端返已经传了的
                //如果isTheSameDir为false，应从根文件处获取已上传的片索引
                if (!isTheSameDir)
                    dirFile = new File(path + File.separator + networkFile.getService() + File.separator + networkFile.getType());
                //从存储目录收集已经上传的切片索引
                List<Integer> uploaded = Arrays.stream(dirFile.listFiles())
                        .filter(file -> file.getName().contains(networkFile.getMd5()))
                        .map(file -> Integer.parseInt(file.getName().split("\\.")[1])).collect(Collectors.toList());
                data.put("uploaded", uploaded);
                //为了继续上传而加入缓存
                existFiles.put(md5, networkFile);
                //文件没传完
                newNetworkFile.setComplete(false);
            }
            //如果不是同一个服务下的同一个类别，记录新文件
            if (!isTheSameDir) INFileService.save(newNetworkFile);
        } else {
            //没上传过，肯定要新增数据库
            NetworkFile newFile = new NetworkFile(
                    originName,
                    param.getMd5() + FileChunkUtil.getFileExtension(originName),
                    service,
                    type,
                    md5,
                    param.getTotalSize());
            newFile.setComplete(false);
            INFileService.save(newFile);
            existFiles.put(md5, newFile);
        }
        return data;
    }

    public Map<String, Object> continueUpload(ChunkFileParam fileParam, FileDoneCallback fileDoneCallback) throws Exception {
        NetworkFile existFile = existFiles.get(fileParam.getMd5());
        String dirPath = path + existFile.getService() + existFile.getType();
        String storeFileName = fileParam.getMd5();  //当前上传文件的名字，加了时间，不会重
        Map<String, Object> data = new HashMap<>();
        try {
            //切片存储
            String fullPath = dirPath + File.separator + storeFileName + "." + fileParam.getCurrentChunkNumber();
            File dest = new File(fullPath);
            dest.createNewFile();
            fileParam.getFile().transferTo(dest.getAbsoluteFile());
            //计算进度
            File[] listFiles = new File(dirPath).listFiles();
            long count = Arrays.stream(listFiles).filter(file -> file.getName().contains(storeFileName)).count();
            double progress = count * 1.0 / fileParam.getTotalChunks();
            boolean completed = progress >= 1.0;
            if (completed) {
                File[] files = new File[fileParam.getTotalChunks()];
                for (int i = 0; i < files.length; i++) {
                    files[i] = new File(dirPath + "/" + storeFileName + "." + (i + 1));
                }
                File file = new File(dirPath + File.separator + storeFileName + FileChunkUtil.getFileExtension(fileParam.getFilename()));
                file.createNewFile();
                FileChunkUtil.merge(file, files, true);
                //上传完成，complete置为true，把链接文件的complete也置为true
                QueryWrapper<NetworkFile> nFileQueryWrapper = new QueryWrapper<>();
                nFileQueryWrapper.eq("md5", fileParam.getMd5());
                List<NetworkFile> networkFiles = INFileService.list(nFileQueryWrapper);
                for (NetworkFile networkFile : networkFiles) {
                    networkFile.setComplete(true);
                    INFileService.updateById(networkFile);
                    if (fileDoneCallback != null) fileDoneCallback.fileDone(networkFile, file);
                }
                //移出缓存
                existFiles.remove(fileParam.getMd5());
                //返回值
                data.put("md5", existFile.getMd5());
                data.put("url", existFile.getService() + File.separator + existFile.getType() + File.separator + existFile.getStoreName());
            }
            data.put("progress", progress);
            data.put("completed", completed);
            return data;
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
    }

}
